<?php

/**
 * SimplifyPHP Framework
 *
 * This file is part of SimplifyPHP Framework.
 *
 * SimplifyPHP Framework is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * SimplifyPHP Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author Rodrigo Rutkoski Rodrigues, <rutkoski@gmail.com>
 */

/**
 *
 *
 * @author Rodrigo Rutkoski Rodrigues, <rutkoski@gmail.com>
 * @package Simplify_Kernel
 */
final class Simplify
{

  public static $DEBUG_ENABLED = false;

  /**
   *
   * @var IApplicationController
   */
  protected static $instance;

  /**
   *
   * @var array
   */
  protected static $actions = array();

  /**
   *
   * @var array
   */
  protected static $exception_handlers = array();

  /**
   * Constructor.
   *
   * @param Config $config
   * @return void
   */
  private function __construct()
  {
  }

  /**
   *
   * @return LanguageImpl
   */
  public static function getL10n()
  {
    try {
      $l10n = self::getInstance()->getComponentInstance('l10n');
    }
    catch (Exception $e) {
      $l10n = self::$instance->registerComponent('l10n', new LanguageImpl);
    }

    return $l10n;
  }

  /**
   *
   * @return Config
   */
  public static function getConfig()
  {
    return self::getInstance()->getConfig();
  }

  /**
   *
   * @return IRouter
   */
  public static function getRouter()
  {
    return self::getInstance()->getRouter();
  }

  /**
   *
   * @return IRequest
   */
  public static function getRequest()
  {
    return self::getInstance()->getRequest();
  }

  /**
   *
   * @return IResponse
   */
  public static function getResponse()
  {
    return self::getInstance()->getResponse();
  }

  /**
   *
   * @return mixed
   */
  public function __call($name, $arguments)
  {
    return self::getInstance()->getComponentInstance($name);
  }

  /**
   *
   *
   * @param string $app_dir the application directory
   * @return IApplicationController
   */
  public static function createInstance($app_dir, $params = null, $id = 'default')
  {
    set_exception_handler(array('Simplify', 'handleException'));

    $config = new Config($params);

    $config->set('sy_dir', SY_DIR);
    $config->set('app_dir', sy_fix_path($app_dir));

    /**
     *
     * Absolute path to the web root
     *
     */
    $config->set('www_dir', sy_fix_path(dirname($_SERVER['SCRIPT_FILENAME'])));

    /**
     *
     * Absolute path to public files
     *
     */
    $config->set('files_dir', '{www_dir}/files');

    /**
     *
     * instantiate core classes
     *
     */

    self::$instance = new AppApplicationController($config);

    self::$instance->setRouter(new AppRouter());
    self::$instance->setRequest(new AppRequest());

    if ($config->has('register_components')) {
      foreach ((array)$config->get('register_components')->getAll() as $name => $component) {
        self::$instance->registerComponent($name, $component);
      }
    }

    /**
     *
     * config
     *
     */

    if (file_exists($app_dir . '/config.php')) {
      require_once ($app_dir . '/config.php');
    }

    self::$instance->config();

    /**
     *
     * init
     *
     */

    self::$instance->initialize();

    return self::$instance;
  }

  /**
   *
   * @return IApplicationController
   */
  public static function getInstance()
  {
    if (empty(self::$instance)) {
      die('You must create an instance of the application with <i>Simplify::createInstance($app_dir)</i>');
    }

    return self::$instance;
  }

  /**
   *
   * @return boolean
   */
  public static function hasInstance()
  {
    return ! empty(self::$instance);
  }

  public static function autoload($class_name)
  {
    $classPath = array(
    	'',
    	'kernel',
    	'kernel/core',
      'kernel/core/api',
    	'kernel/data',
    	'kernel/data/filter',
    	'kernel/data/validation',
      'kernel/view',
      'kernel/view/filters',
      'kernel/cache',
      'kernel/dao',
      'kernel/domain',
      'kernel/domain/association',
      'kernel/domain/definition',
      'kernel/domain/api',
      'kernel/domain/exception',
      'kernel/adb',
      'lib'
    );

    foreach ($classPath as $path) {
      if (file_exists(SY_DIR . '/' . $path . '/' . $class_name . '.php')) {
        require_once (SY_DIR . '/' . $path . '/' . $class_name . '.php');
        return;
      }
    }

    $app_class_path = Config::parseFrom(self::getConfig()->get('app_class_path'))->getAll();

    foreach ($app_class_path as $path) {
      if (file_exists($path . '/' . $class_name . '.php')) {
        require_once ($path . '/' . $class_name . '.php');
        return;
      }
    }
  }

  public static function addAction($hook, $callback)
  {
    if (! isset(self::$actions[$hook])) {
      self::$actions[$hook] = array();
    }

    self::$actions[$hook][] = $callback;
  }

  public static function callAction($hook)
  {
    $args = func_get_args();
    array_shift($args);

    self::callActionArray($hook, $args);
  }

  public static function callActionArray($hook, $args)
  {
    if (isset(self::$actions[$hook])) {
      foreach (self::$actions[$hook] as $callback) {
        call_user_func_array($callback, $args);
      }
    }
  }

  public static function setExceptionHandler($exception, $callback)
  {
    if (! isset(self::$exception_handlers[$exception])) {
      self::$exception_handlers[$exception] = array();
    }

    self::$exception_handlers[$exception] = $callback;
  }

  public static function hasExceptionHandler($exception)
  {
    if ($exception instanceof Exception) {
      $exception = get_class($exception);
    }

    return isset(self::$exception_handlers[$exception]);
  }

  public static function callExceptionHandler(Exception $exception)
  {
    $type = get_class($exception);

    $callback = self::$exception_handlers[$type];

    return call_user_func($callback, $exception);
  }

  public static function handleException(Exception $e)
  {
    static $recursions;

    $recursions++;

    if ($recursions >= 100) {
      die(self::outputException(new Exception('Too many recursions')));
    }

    if (self::hasExceptionHandler($e)) {
      try {
        self::callExceptionHandler($e);
      }
      catch (Exception $e) {
        die(self::outputException($e));
      }
    }
    else {
      die(self::outputException($e));
    }
  }

  public static function outputException($e)
  {
    if (SY_DEBUG_LEVEL && Simplify::hasInstance() && Simplify::getRequest()->json()) {
      header('Content-type: application/json; charset="utf-8"');
      $response = array('exception' => array('class' => get_class($e), 'message' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'file' => $e->getFile(), 'line' => $e->getLine()));
      return json_encode($response);
    }

    $output = '<html><body id="sy_error">';
    $output .= '<h2>Simplify PHP</h2><p><b>' . $e->getMessage() . '</b></p><p>';
    $output .= '<pre>' . htmlentities($e->getTraceAsString()) . '</pre>';
    $output .= '</p>';
    $output .= '<p>' . get_class($e) . ' thrown in ' . $e->getFile() . ' at line ' . $e->getLine() . '</p>';
    $output .= '</body></html>';

    return $output;
  }

}

?>